package com.axonactive.yolo.service.internal;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;

import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.Parameters;
import com.axonactive.yolo.service.api.TravelPayoutsAPI;
import com.axonactive.yolo.service.api.WegoAPI;
import com.axonactive.yolo.util.CONSTANTS;
import com.axonactive.yolo.util.IOUtil;
import com.axonactive.yolo.util.Utils;

/**
 * Provide the FlightRequester objects to send request to the server.
 * 
 * @author pqtrung
 * 
 */
public class FlightRequesterService {
	private List<Flight> flights;
	private static final Logger LOGGER = Logger.getLogger(FlightRequesterService.class);
	private static FlightRequesterService INSTANCE = null;
	private Parameters param;

	public static FlightRequesterService getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new FlightRequesterService();
		}
		return INSTANCE;
	}

	/***
	 * Wrapper class it sends request with the input parameters to server.
	 * 
	 * @param departureAirportCode
	 *            required - 3-letter IATA airport code (e.g. SIN)
	 * @param arrivalAirportCode
	 *            required - 3-letter IATA airport code (e.g. BKK)
	 * @param tripType
	 *            required - Possible Values: oneWay, roundTrip, default is
	 *            roundTrip
	 * @param cabinClass
	 *            required - Possible Values: Economy, Business, First, default
	 *            is Economy
	 * @param inboundDate
	 *            yyyy-MM-dd (not required for oneWay flights)
	 * @param outboundDate
	 *            required - yyyy-MM-dd
	 * @param numAdults
	 *            required - 1- 9: default is 1
	 * @param numChildren
	 *            required - 0- 6: default is 0
	 * @param numInfants
	 *            required - 0- 6: default is 0
	 * @param rangValue
	 *            0 if departure/returns dates are exact or 1 for flexible dates
	 *            (+/-3 days)
	 * @param currencyCode
	 *            the code of the current currency
	 * @return list of com.axonactive.yolo.domain.Flight
	 */
	public List<Flight> sendRequestToWego(String departureAirportCode, String arrivalAirportCode, String tripType, String cabinClass, String inboundDate, String outboundDate, String numAdults, String numChildren, String numInfants, String rangValue, String currencyCode) {
		List<Flight> flights = new ArrayList<Flight>();

		try {
			FlightFactoryService factoryService = new FlightFactoryService();
			FlightRequester wegoRequester = new WegoAPI();
			wegoRequester.setRequest(departureAirportCode, arrivalAirportCode, tripType, cabinClass, inboundDate, outboundDate, numAdults, numChildren, numInfants, rangValue, currencyCode);
			String response = wegoRequester.sendRequest();
			// String response =
			// IOUtil.readJSONResponseFromFile("E:\\Research\\JSOn\\wego.txt");
			flights = wegoRequester.processResponse(response);
			LOGGER.info("size of flights from wego:" + flights.size());
			flights = factoryService.removeTheDoublingFlights(flights);
			LOGGER.info("size of flights from wego after remove the doubling flights:" + flights.size());
		} catch (Exception e) {
			LOGGER.info("SendRequetToWego Exception", e);
			return new ArrayList<Flight>();
		}
		return flights;
	}

	/***
	 * Wrapper class it sends request with the input parameters to server.
	 * 
	 * @param departureAirportCode
	 *            required - 3-letter IATA airport code (e.g. SIN)
	 * @param arrivalAirportCode
	 *            required - 3-letter IATA airport code (e.g. BKK)
	 * @param tripType
	 *            required - Possible Values: oneWay, roundTrip, default is
	 *            roundTrip
	 * @param cabinClass
	 *            required - Possible Values: Economy, Business, First, default
	 *            is Economy
	 * @param inboundDate
	 *            yyyy-MM-dd (not required for oneWay flights)
	 * @param outboundDate
	 *            required - yyyy-MM-dd
	 * @param numAdults
	 *            required - 1- 9: default is 1
	 * @param numChildren
	 *            required - 0- 6: default is 0
	 * @param numInfants
	 *            required - 0- 6: default is 0
	 * @param rangValue
	 *            0 if departure/returns dates are exact or 1 for flexible dates
	 *            (+/-3 days)
	 * @param currencyCode
	 *            the code of the current currency
	 * @return list of com.axonactive.yolo.domain.Flight
	 */
	// @CacheEvict(value = "listFlightFromTravelPayoutCache", beforeInvocation =
	// true)
	// @Cacheable(value="listFlightFromTravelPayoutCache", key="")
	public List<Flight> sendRequestToTravelPayout(String departureAirportCode, String arrivalAirportCode, String tripType, String cabinClass, String inboundDate, String outboundDate, String numAdults, String numChildren, String numInfants, String rangValue, String currencyCode) {
		List<Flight> flights = new ArrayList<Flight>();
		try {
			FlightFactoryService factoryService = new FlightFactoryService();
			factoryService.setParameter(param);
			LOGGER.info("Send request to Travel Payout");
			TravelPayoutsAPI travelPayoutRequester = new TravelPayoutsAPI();
			travelPayoutRequester.setRequest(departureAirportCode, arrivalAirportCode, tripType, cabinClass, inboundDate, outboundDate, numAdults, numChildren, numInfants, rangValue, currencyCode);
			String response = travelPayoutRequester.sendRequest();
			// LOGGER.info(" Thread " + Thread.currentThread().getId()
			// + " sendRequestToTravelPayout:" + response);
			flights = travelPayoutRequester.processResponse(response);
			// LOGGER.info("Size of fligts travelpayout: " + flights.size());
			flights = factoryService.removeTheDoublingFlights(flights);
			// LOGGER.info("Size of flights after remove doubling flights of travelpayout: "
			// + flights.size());
			flights = factoryService.removeTheWrongFlights(flights);
//			LOGGER.info("Size of flights after remove wrong fligths of travelpayout: " + flights.size());
			return flights;
		} catch (Exception e) {
			LOGGER.info("SendRequetToTravelPayout Exception", e);
			LOGGER.info("sendRequestToTravelPayout - exception : " + e.getMessage());
			return new ArrayList<Flight>();
		}
	}

	// @CacheEvict(value = "listFlightFromMockDBCache", beforeInvocation = true)
	// @Cacheable(value="listFlightFromMockDBCache", key="")
	public List<Flight> sendRequestToMockDB(String departureAirportCode, String arrivalAirportCode, String tripType, String cabinClass, String inboundDate, String outboundDate, String numAdults, String numChildren, String numInfants, String rangValue, String currencyCode, HttpServletRequest httpRequest) {
		try {
			FlightRequester wegoRequester = new WegoAPI();
			wegoRequester.setRequest(departureAirportCode, arrivalAirportCode, tripType, cabinClass, inboundDate, outboundDate, numAdults, numChildren, numInfants, rangValue, currencyCode);
			String response = wegoRequester.sendRequestToMockDB(httpRequest);
			LOGGER.info(response);
			flights = wegoRequester.processResponse(response);
			return flights;
		} catch (Exception e) {
			LOGGER.info("Error", e);
		}
		return null;
	}

	// @CacheEvict(value = "listFlightFromMockDBTravelCache", beforeInvocation =
	// true)
	// @Cacheable(value="listFlightFromMockDBTravelCache", key="")
	public List<Flight> sendRequestToMockDBTravel(String departureAirportCode, String arrivalAirportCode, String tripType, String cabinClass, String inboundDate, String outboundDate, String numAdults, String numChildren, String numInfants, String rangValue, String currencyCode, HttpServletRequest httpRequest) throws Exception {
		FlightRequester travelPayoutRequester = new TravelPayoutsAPI();
		travelPayoutRequester.setRequest(departureAirportCode, arrivalAirportCode, tripType, cabinClass, inboundDate, outboundDate, numAdults, numChildren, numInfants, rangValue, currencyCode);
		String response = travelPayoutRequester.sendRequestToMockDB(httpRequest);
		flights = travelPayoutRequester.processResponse(response);
		return flights;
	}

	// send Request for getting jason data
	// @Cacheable(value="listFlightForSearchCache", key="")
	public List<Flight> sendRequest(Parameters param) throws ParseException {
		List<Flight> flights = new ArrayList<Flight>();
		FlightFactoryService factoryService = new FlightFactoryService();
		this.setParameter(param);
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
		String formattedOB = null;
		String formattedIB = null;
		formattedOB = sdf2.format(sdf1.parse(param.getOutBound().trim()));
		LOGGER.info(formattedOB);
		if (param.getTypeOfTrip().equals("roundTrip")) {
			formattedIB = sdf2.format(sdf1.parse(param.getInBound().trim()));
			LOGGER.info(formattedIB);
		} else if (param.getTypeOfTrip().equals("oneWay")) {
			formattedIB = "";
			LOGGER.info(formattedIB);
		}
		List<Flight> travelPayoutFlights;
		// if request is sent from search
		travelPayoutFlights = sendRequestToTravelPayout((param.getFromLocation().substring(0, 3)), param.getToLocation().substring(0, 3), param.getTypeOfTrip(), param.getCabinClass(), formattedIB, formattedOB, param.getNumAdult(), param.getNumChild(), param.getNumInfant(), "", CONSTANTS.USD);
		//
		factoryService.addToRepository(travelPayoutFlights);
		// get flight after compare between the providers and filter by stops
		flights = factoryService.getFlightsAfterComparedFromSourcesAndFilterByStop("" + param.getNumStop());
		LOGGER.info("Size of flights after filter by stop: " + flights.size());
		// get flight after filter by airlines
		if (param.getAirlines() != null && !"".equals(param.getAirlines().trim())) {
			flights = factoryService.getFlightAfterFilterByAirlines(flights, param.getAirlines(), param.getAirlinesFilterMode());
			LOGGER.info("Size of flights after filter by airlines: " + flights.size());
		}
		return flights;
	}

	public List<Flight> sendRequestForTask(Parameters param) throws ParseException {
		List<Flight> flights = new ArrayList<Flight>();
		FlightFactoryService factoryService = new FlightFactoryService();
		this.setParameter(param);
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
		String formattedOB = null;
		String formattedIB = null;
		formattedOB = sdf2.format(sdf1.parse(param.getOutBound().trim()));
		LOGGER.info(formattedOB);
		if (param.getTypeOfTrip().equals("roundTrip")) {
			formattedIB = sdf2.format(sdf1.parse(param.getInBound().trim()));
			LOGGER.info(formattedIB);
		} else if (param.getTypeOfTrip().equals("oneWay")) {
			formattedIB = "";
			LOGGER.info(formattedIB);
		}
		List<Flight> travelPayoutFlights;
		// if request is sent from search
		travelPayoutFlights = sendRequestToTravelPayout((param.getFromLocation().substring(0, 3)), param.getToLocation().substring(0, 3), param.getTypeOfTrip(), param.getCabinClass(), formattedIB, formattedOB, param.getNumAdult(), param.getNumChild(), param.getNumInfant(), "", CONSTANTS.USD);

		return travelPayoutFlights;
	}

	// @Cacheable(value="listFlightForTaskSearchCache", key="")
	public List<Flight> sendRequestFromTask(Parameters param) throws ParseException {
		List<Flight> flights = new ArrayList<Flight>();
		FlightFactoryService factoryService = new FlightFactoryService();
		this.setParameter(param);
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
		String formattedOB = null;
		String formattedIB = null;
		formattedOB = sdf2.format(sdf1.parse(param.getOutBound().trim()));
		LOGGER.info(formattedOB);
		if (param.getTypeOfTrip().equals("roundTrip")) {
			formattedIB = sdf2.format(sdf1.parse(param.getInBound().trim()));
			LOGGER.info(formattedIB);
		} else if (param.getTypeOfTrip().equals("oneWay")) {
			formattedIB = "";
			LOGGER.info(formattedIB);
		}
		List<Flight> travelPayoutFlights = sendRequestToTravelPayout(param.getFromLocation(), param.getToLocation(), param.getTypeOfTrip(), param.getCabinClass(), param.getInBound(), param.getOutBound(), "1", "0", "0", param.getFlexibleDays(), param.getCurrencyCode());

		// add to Repository
		factoryService.addToRepository(travelPayoutFlights);

		// convert and filter by MaxPrice
		double maxPrice = Utils.convertToDouble(param.getMaxPrice());
		flights = factoryService.convertAndFilterAmountByInputCurrency(param.getCurrencyCode(), maxPrice);
		LOGGER.info("Size of flights after filter by maxPrice : " + flights.size());

		// get flight after compare between the providers and filter by stops
		// flights = factoryService
		// .getFlightsAfterComparedFromSourcesAndFilterByStop(""
		// + param.getNumStop());
		// LOGGER.info("Size of flights after filter by stop: " +
		// flights.size());
		// get flight after filter by airlines
		if (param.getAirlines() != null && !"".equals(param.getAirlines().trim())) {
			flights = factoryService.getFlightAfterFilterByAirlines(flights, param.getAirlines(), CONSTANTS.LIKE);
			LOGGER.info("Size of flights after filter by airlines: " + flights.size());
		}

		return flights;
	}

	public void setParameter(Parameters param) {
		this.param = param;
	}

	// @Cacheable(value="listFlightForSearchFromMockDBCache", key="")
	public List<Flight> sendRequestMockDB(Parameters param, HttpServletRequest httpRequest) throws Exception {
		FlightFactoryService factoryService = new FlightFactoryService();
		this.setParameter(param);
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
		String formattedOB = null;
		String formattedIB = null;
		formattedOB = sdf2.format(sdf1.parse(param.getOutBound().trim()));
		LOGGER.info(formattedOB);
		if (CONSTANTS.ROUND_TRIP.equals(param.getTypeOfTrip())) {
			formattedIB = sdf2.format(sdf1.parse(param.getInBound().trim()));
			LOGGER.info(formattedIB);
		} else if (CONSTANTS.ONE_WAY.equals(param.getTypeOfTrip())) {
			formattedIB = "";
			LOGGER.info(formattedIB);
		}
		List<Flight> wegoFlights = sendRequestToMockDB((param.getFromLocation().substring(0, 3)), param.getToLocation().substring(0, 3), param.getTypeOfTrip(), "", formattedIB, formattedOB, param.getNumAdult(), param.getNumChild(), param.getNumInfant(), "", CONSTANTS.USD, httpRequest);
		if (wegoFlights != null) {
			factoryService.addToRepository(wegoFlights);
		} else {
			List<Flight> travelPayoutFlights = sendRequestToMockDBTravel((param.getFromLocation().substring(0, 3)), param.getToLocation().substring(0, 3), param.getTypeOfTrip(), "", formattedIB, formattedOB, param.getNumAdult(), param.getNumChild(), param.getNumInfant(), "", CONSTANTS.USD, httpRequest);
			if (travelPayoutFlights != null) {
				factoryService.addToRepository(travelPayoutFlights);
			} else
				return null;
		}

		return factoryService.getFlightsAfterComparedFromSourcesAndFilterByStop("" + param.getNumStop());
	}

}